﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Web;


namespace WorldDomination.GoogleApi.AjaxSearch
{
    [DataContract]
    public class Search
    {
        #region Properties

        [DataMember(Name = "responseData")]
        public ResponseData ResponseData { get; internal set; }

        [DataMember(Name = "responseDetails")]
        public string ResponseDetails { get; internal set; }

        [DataMember(Name = "responseStatus")]
        public int ResponseStatus { get; internal set; }

        #endregion

        #region Methods

        private static int DetermineNumberOfHitsToApi(int numberOfResults)
        {
            int numberOfHits;
            int startPage;


            // Determine how many times we need to hit google to get the results.
            startPage = numberOfResults - 8;

            if (startPage >= 24)
            {
                // Page 4.
                numberOfHits = 4;
            }
            else if (startPage <= 23 &&
                startPage >= 16)
            {
                // Page 3.
                numberOfHits = 3;
            }
            else if (startPage <= 15 &&
                startPage >= 8)
            {
                // Page 2.
                numberOfHits = 2;
            }
            else
            {
                // Page 1.
                numberOfHits = 1;
            }

            return numberOfHits;
        }

        private static int DetermineStartPageIndex(int currentIteration)
        {
            int startPage;


            switch (currentIteration)
            {
                case 1: startPage = 8; break;
                case 2: startPage = 16; break;
                case 3: startPage = 24; break;
                default: startPage = 0; break;
            }

            return startPage;
        }

        public static Search GetSearchResults(string keywords,
            string referer)
        {
            return Search.GetSearchResults(keywords, 
                referer,
                null,
                32,
                null);
        }

        public static Search GetSearchResults(string keywords,
            string referer,
            int numberOfResults)
        {
            return Search.GetSearchResults(keywords,
                referer,
                null,
                numberOfResults,
                null);
        }

        public static Search GetSearchResults(string keywords,
            string referer,
            string siteRestriction,
            int numberOfResults,
            string apiKey)
        {
            HttpWebRequest httpWebRequest;
            string jsonResult = null;
            string requestUriString;
            DataContractJsonSerializer dataContractJsonSerializer;
            Search tempSearch;
            Search search = null;


            if (string.IsNullOrEmpty(keywords))
            {
                throw new ArgumentNullException("keywords");
            }
            else if (string.IsNullOrEmpty(referer))
            {
                throw new ArgumentNullException("referer");
            }
            else if (!referer.StartsWith("http://", 
                StringComparison.InvariantCultureIgnoreCase))
            {
                throw new InvalidOperationException("Referer has to start with 'http://'. Eg. http://www.YouSite.com");
            }

            for (int i = 0; i < Search.DetermineNumberOfHitsToApi(numberOfResults); i++)
            {
                // Search for any content in the site, based upon the keywords, from the Google Ajax Search API.
                requestUriString = string.Format("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=large&start={0}&q={1}{2}{3}",
                    Search.DetermineStartPageIndex(i),
                    HttpUtility.UrlEncode(keywords),
                    !string.IsNullOrEmpty(siteRestriction) ? " site:" + siteRestriction : string.Empty,
                    !string.IsNullOrEmpty(apiKey) ? "&key=" + apiKey : string.Empty);

                httpWebRequest = WebRequest.Create(requestUriString) as HttpWebRequest;
                httpWebRequest.Referer = referer;

                if (httpWebRequest != null)
                {
                    // Connect and retrieve the data.
                    using (HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse)
                    {
                        if (httpWebResponse != null)
                        {
                            using (StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream()))
                            {
                                jsonResult = streamReader.ReadToEnd();
                            }
                        }
                    }
                }

                // Do we have any data from Google?
                if (!string.IsNullOrEmpty(jsonResult))
                {
                    dataContractJsonSerializer = new DataContractJsonSerializer(typeof(Search));

                    using (MemoryStream memoryStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(jsonResult)))
                    {
                        tempSearch = dataContractJsonSerializer.ReadObject(memoryStream) as Search;
                    }

                    if (tempSearch != null &&
                        tempSearch.ResponseData != null &&
                        tempSearch.ResponseData.Results != null &&
                        tempSearch.ResponseData.Results.Count() > 0)
                    {
                        // We have some result data!
                        if (search == null)
                        {
                            search = tempSearch;
                        }
                        else
                        {
                            IList<Result> resultList;

                            resultList = new List<Result>(search.ResponseData.Results);

                            // Lets append the current search results to the existing results.
                            foreach (Result result in tempSearch.ResponseData.Results)
                            {
                                resultList.Add(result);
                            }

                            // Set the new result list.
                            search.ResponseData.Results = resultList.ToArray();
                        }


                        if (tempSearch.ResponseData != null &&
                            tempSearch.ResponseData.Results != null &&
                            tempSearch.ResponseData.Results.Count() < 8)
                        {
                            // We didn't max out result set out.. so lets not iterate.
                            break;
                        }
                    }
                    else
                    {
                        // No results, so don't bother iterating.
                        break;
                    }
                }
            }

            return search;
        }

        public override string ToString()
        {
            return this.ResponseData == null ||
                this.ResponseData.Results == null ||
                this.ResponseData.Results.Length <= 0 ? "No results retrieved." :
                string.Format("# Results: {0}. Page: {1}",
                    this.ResponseData.Results.Length,
                    this.ResponseData.Cursor.CurrentPageIndex);
        }

        #endregion
    }
}